package com.spzx.order.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.rabbitmq.client.Channel;
import com.spzx.common.rabbitmq.SpzxRabbitmqService;
import com.spzx.common.rabbitmq.constants.SpzxRabbitmqConstants;
import com.spzx.order.api.domain.OrderInfo;
import com.spzx.order.service.IOrderInfoService;
import com.spzx.order.service.IOrderLogService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
@Slf4j
public class SpzxOrderListener {
    @Resource
    IOrderInfoService orderInfoService;

    @Resource
    StringRedisTemplate stringRedisTemplate;
    /*
            点对点模式： 每个消息只能被消费一次
                一个生产者  一个队列  一个消费者
            发布订阅模式：
                    spzx-payment 支付回调发送的消息
                            -  spzx-order获取使用： 更新订单的支付状态
                            -  spzx-product获取使用： 释放库存/更新库存销量
                 一个交换机 多个队列 多个消费者  每个队列都可以接收到交换机的消息，每个队列的同一个消息都可以被消费一次
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    exchange = @Exchange(value = "spzx.payment.exchange",type = ExchangeTypes.TOPIC),
                    value = @Queue(value = "spzx.order.payment.notity.queue"),
                    key = {"spzx.payment.nofity"}
            )
    })
    public void paymentNotify(Map<String,String> messageMap , Message msg , Channel channel){
        try {
            //业务：更新订单状态
            String type = messageMap.get("type");
            String orderNo = messageMap.get("orderNo");
            OrderInfo orderInfo = orderInfoService.getOne(Wrappers.lambdaQuery(OrderInfo.class)
                    .eq(OrderInfo::getOrderNo, orderNo));
            if(orderInfo==null){
                log.error("更新订单支付状态失败，订单{} 查询失败",orderNo);
                return;
            }
            orderInfo.setOrderStatus(type.equals("1")?1L:-1L);
            orderInfo.setCancelReason(type.equals("1")?"支付成功":"支付失败或者取消支付");
            orderInfoService.updateById(orderInfo);
            // 如果涉及了优惠券和积分
            // 订单支付成功： 无需操作
            // 订单支付失败： 释放锁定的优惠券，扣除之前提交订单时的积分
            channel.basicAck(msg.getMessageProperties().getDeliveryTag() , false);
        } catch (Exception e) {
            try {
                if(msg.getMessageProperties().isRedelivered()){
                    channel.basicReject(msg.getMessageProperties().getDeliveryTag() ,false);
                }else{
                    channel.basicNack(msg.getMessageProperties().getDeliveryTag() ,false,true);
                }
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }

    }


    @RabbitListener(queues = {SpzxRabbitmqConstants.ORDER_DELAY_QUEUE})
    public void closeOrder(String message , Message msg , Channel channel){
        JSONObject jsonObject = JSON.parseObject(message, JSONObject.class);
        String orderId = jsonObject.getString("orderId");

        try {
           orderInfoService.closeOrder(orderId);
            //2、手动ack
            channel.basicAck(msg.getMessageProperties().getDeliveryTag() , false);
        } catch (Exception e) {
            try {
                if(!msg.getMessageProperties().isRedelivered()){
                    stringRedisTemplate.delete("spzx:order:update:lock:"+orderId);
                    //3、判断：如果第一次消费异常 重新归队再次消费
                    channel.basicNack(msg.getMessageProperties().getDeliveryTag() , false,true);
                }else{
                    //4、第二次消费异常，丢弃消息(如果队列绑定了死信队列  消息会进入死信队列)
                    channel.basicReject(msg.getMessageProperties().getDeliveryTag(),false);
                }
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }


    }
}
